package edos.prm.artifact;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

/**
 * A {@code Directory} is a combination of attributes of different types.
 * <p>
 * A directory is in fact a mapping from contexts to artifact sets. A context
 * is a tag represented by a String.
 * 
 * @author Ciaran Bryce
 * @version 1.0
 */
public final class Directory {
	// The type of each element set of the range can be different !!!
	HashMap<String, ArtifactSet<? extends Artifact>> map;

	/**
	 * Create an empty directory
	 */
	public Directory() {
		map = new HashMap<String, ArtifactSet<? extends Artifact>>();
	}
	
	/**
	 * Add a set of artifacts to the directory with a given context key
	 * <p>
	 * If the key was in use before, then the original set is over-written.
	 * 
	 * @param context a string name for the attributes
	 * @param set a set of artifacts
	 */
	public synchronized <T extends Artifact>
	void add(String context, ArtifactSet<T> set) {
		map.put(context, set);
	}
	
	/**
	 * Remove an artifact set from the directory with the given key
	 * 
	 * @param context the context from which artifacts are being removed
	 */
	public synchronized void remove(String context) {
		map.remove(context);
	}
	
	/**
	 * Adds a given directory object to this one.
	 * 
	 * @param dir a directory object to add to the current directory
	 * @throws IllegalArgumentException if the argument dir possesses a key
	 * with the same name
	 */
	public synchronized void add(Directory dir) {
		for ( String context : dir.map.keySet() ) {
			if ( map.containsKey(context) )
				throw new IllegalArgumentException("Key " + context + 
						" is already in use.");
			this.add(context, dir.map.get(context));
		}
	}
	
	/**
	 * Select an artifact set that represents the specified context
	 * <p>
	 * @param context a String key denoting the context
	 * @return an artifact set
	 * @throws IllegalArgumentException if the key is not used in the directory
	 */
	public synchronized ArtifactSet<? extends Artifact> select(String context) {
		ArtifactSet<? extends Artifact> set = map.get(context);
		if ( set != null )
			return set;
		throw new IllegalArgumentException("Undefined key");
	}
		
	/**
	 * Return true if the argument directory matches this directory object.
	 * 
	 * @param dir the directory being compared to
	 * @return true if this directory matches dir
	 */
	public boolean matches(Directory dir)  {
		for ( String context : map.keySet() ) {
			ArtifactSet<? extends Artifact> thisSet = map.get(context);
			ArtifactSet<? extends Artifact> dirSet = dir.map.get(context);
			ArtifactSet<Artifact> set1 = new ArtifactSet<Artifact>();
			ArtifactSet<Artifact> set2 = new ArtifactSet<Artifact>();
			for ( Artifact art : thisSet.getContents() )
				set1.add(art);
			for ( Artifact art : dirSet.getContents() )
				set2.add(art);
			if ( !set1.matches(set2) )
				return false;
		}
		return true;
	}
	
	/**
	 * Return the set of contexts in this directory
	 * 
	 * @return a set of context strings
	 */
	public synchronized Set<String> getContextList() {
		HashSet<String> result = new HashSet<String>();
		result.addAll(map.keySet());
		return result;
	}
	
	/**
	 * Return the whole set of artifacts in this directory
	 * 
	 * @return an ArtifactSet<Artifact> of all elements in this directory
	 */
	public synchronized ArtifactSet<Artifact> getAllElements() {
		ArtifactSet<Artifact> result = new ArtifactSet<Artifact>();
		for (String ctxt : this.getContextList() ) {
			ArtifactSet<? extends Artifact> set = map.get(ctxt);
			for (Artifact art : set.getContents() )
				result.add(art);
		}
		return result;
	}
}
